/*
 * Copyright (c) 2022, IMMORTA Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of IMMORTA Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef OS_ADAPTER_H
#define OS_ADAPTER_H

/*!
 * @file os_adapter.h
 * @brief This file is adapter to RTOS
 */

/*******Includes***************************************************************/
#include <stddef.h>
#include <stdbool.h>
#include "device_registers.h"
#include "errcodes.h"

/*******Definitions************************************************************/
#define OS_ADAPTER_WAIT_FOREVER 0xFFFFFFFFU

#ifdef USING_OS_FREERTOS
/* FreeRTOS implementation */
#include "FreeRTOS.h"
#include "semphr.h"

typedef void (*os_adapter_task_t)(void* param);
#if configSUPPORT_STATIC_ALLOCATION == 1
typedef struct {
    SemaphoreHandle_t handle;  /*!< Semaphore handler*/
    StaticSemaphore_t buffer;  /*!< Point to a variable which will hold the semaphore's data structure*/
} semaphore_t;
typedef semaphore_t mutex_t;
#else /* configSUPPORT_STATIC_ALLOCATION == 0, it's dynamic allocation */
/*! @brief Type for a mutex. */
typedef SemaphoreHandle_t mutex_t;
/*! @brief Type for a semaphore. */
typedef SemaphoreHandle_t semaphore_t;
typedef QueueHandle_t queue_t;
#endif /* configSUPPORT_STATIC_ALLOCATION == 1 */
#elif defined USING_OS_UCOS
#include "ucos_ii.h"
/* ucos-ii implementation */
/*! @brief Type for a semaphore. */
typedef OS_EVENT* semaphore_t;
/*! @brief Type for a mutex. */
typedef struct {
    OS_EVENT*  event;     /*!< a pointer to the event control clock associated with the created mutex*/
    uint8_t    prio;      /*!< the priority to use when accessing the mutual exclusion semaphore*/
} mutex_t;
#else
/* Bare-metal implementation */
/*! @brief Type for a mutex. */
typedef uint8_t mutex_t;
/*! @brief Type for a semaphore. */
typedef volatile uint8_t semaphore_t;
#endif

/*!
 * @brief Time uint
 */
typedef enum {
    OS_ADAPTER_TIME_UNIT_MS = 0,                /*!< Time unit is ms*/
    OS_ADAPTER_TIME_UNIT_TICK = 1               /*!< Time unit is ticks*/
} os_adapter_time_unit_t;

/*! @brief systick callback type */
typedef void (*os_adapter_tick_callback_t)(void* param);
/*!
* @brief baremetal configuration structure
*/
typedef struct {
    os_adapter_tick_callback_t  tickCallback;     /*!< User callback */
    void*                       callbackParam;    /*!< Parameter for the callback function */
} os_adapter_baremetal_config_t;

/*******APIs*******************************************************************/
#if defined(__cplusplus)
extern "C" {
#endif


uint32_t OS_Adapter_GetCurrentTick(void);

/*!
 * @brief Initialize system tick clock
 *
 * @param[in] config: user systick callback configuration
 * @return None
 */
void OS_Adapter_Init(os_adapter_baremetal_config_t* config);

/*!
 * @brief Deinitialize system tick clock
 *
 * @param[in] None
 * @return None
 */
void OS_Adapter_Deinit(void);

/*!
 * @brief Delays execution for a number of milliseconds(ticks)
 *
 * @param[in] mode: time uint,milliseconds or tick
 * @param[in] delay: delay Time delay in milliseconds or ticks
 * @return None
 */
void OS_Adapter_DelayTime(os_adapter_time_unit_t mode, uint32_t delay);

/*!
 * @brief Returns the number of miliseconds(ticks) elapsed since starting the internal timer
 * or starting the scheduler.
 *
 * @param[in] mode: time uint,milliseconds or tick
 * @return the number of miliseconds(ticks) elapsed
 */
uint32_t OS_Adapter_GetTimeElapsed(os_adapter_time_unit_t mode);

/*!
 * @brief Creates a semaphore with a given value.
 *
 * @param[in] semaphore reference to the semaphore object
 * @param[in] initValue initial value of the semaphore
 * @return  One of the possible status codes:
 * - ERR_SUCCESS: semaphore created
 * - ERR_ERROR: semaphore could not be created
 */
errcode_t OS_Adapter_CreateSemaphore(semaphore_t* semaphore, uint8_t initValue);

/*!
 * @brief Increment a semaphore
 *
 * @param[in] semaphore reference to the semaphore object
 * @return  One of the possible status codes:
 * - ERR_SUCCESS: semaphore post operation success
 * - ERR_ERROR: semaphore could not be incremented
 */
errcode_t OS_Adapter_PostSemaphore(semaphore_t* semaphore);

/*!
 * @brief Decrement a semaphore with timeout
 *
 * @param[in] semaphore reference to the semaphore object
 * @param[in] timeout time-out value in milliseconds
 * @return  One of the possible status codes:
 * - ERR_SUCCESS: semaphore wait operation success
 * - ERR_ERROR: semaphore wait timed out
 */
errcode_t OS_Adapter_WaitSemaphore(semaphore_t* semaphore, uint32_t timeout);

/*!
 * @brief Destroys a previously created mutex
 *
 * @param[in] semaphore reference to the mutex object
 * @return  One of the possible status codes:
 * - ERR_SUCCESS: mutex destroyed
 */
errcode_t OS_Adapter_DestroySemaphore(semaphore_t* semaphore);

/*!
 * @brief Create an unlocked mutex
 *
 * @param[in] mutex reference to the mutex object
 * @return  One of the possible status codes:
 * - ERR_SUCCESS: mutex created
 * - ERR_ERROR: mutex could not be created
 */
errcode_t OS_Adapter_CreateMutex(mutex_t* mutex);

/*!
 * @brief Unlocks a previously locked mutex
 *
 * @param[in] mutex reference to the mutex object
 * @return  One of the possible status codes:
 * - ERR_SUCCESS: mutex unlock operation success
 * - ERR_ERROR: mutex unlock failed
 */
errcode_t OS_Adapter_UnlockMutex(mutex_t* mutex);

/*!
 * @brief Waits for a mutex and locks it
 *
 * @param[in] mutex reference to the mutex object
 * @param[in] timeout time-out value in milliseconds
 * @return  One of the possible status codes:
 * - ERR_SUCCESS: mutex lock operation success
 * - ERR_ERROR: mutex already owned by current thread
 * - ERR_TIMEOUT: mutex lock operation timed out
 */
errcode_t OS_Adapter_LockMutex(mutex_t* mutex, uint32_t timeout);

/*!
 * @brief Destroys a previously created mutex
 *
 * @param[in] pMutex reference to the mutex object
 * @return  One of the possible status codes:
 * - ERR_SUCCESS: mutex destroyed
 */
errcode_t OS_Adapter_DestroyMutex(mutex_t* mutex);

#ifdef USING_OS_FREERTOS
#if (configSUPPORT_STATIC_ALLOCATION == 1)
/*!
 * @brief Create a task
 *
 * @param[in] task: task function
 * @param[in] param: the pointer to task parameters
 * @param[in] taskName: task name
 * @param[in] stackBuffer: stack buffer
 * @param[in] stackDepth: task stack depth
 * @param[in] priority: task priority
 * @param[in] taskBuffer: the pointer to task control block
 * @param[out] taskHandler: task handle
 * @return  status codes:
 * - ERR_SUCCESS: create task successfully
 * - ERR_ERROR: create task unsuccessful
 */
errcode_t OS_Adapter_CreateTask(os_adapter_task_t task,
                                void* param,
                                const char* taskName,
                                uint32_t stackBuffer
                                uint32_t stackDepth,
                                uint32_t priority,
                                StaticTask_t* taskBuffer,
                                TaskHandle_t* taskHandle);
#else
/*!
 * @brief Create a task
 *
 * @param[in] task: task function
 * @param[in] param: the pointer to task parameters
 * @param[in] taskName: task name
 * @param[in] stackDepth: task stack depth
 * @param[in] priority: task priority
 * @param[out] taskHandle: task handle
 * @return  status codes:
 * - ERR_SUCCESS: create task successfully
 * - ERR_ERROR: create task unsuccessful
 */
errcode_t OS_Adapter_CreateTask(os_adapter_task_t task,
                                void* param,
                                const char* taskName,
                                uint32_t stackDepth,
                                uint32_t priority,
                                TaskHandle_t* taskHandle);

/*!
 * @brief Create a queue
 *
 * @param[in] queueLength: queue length
 * @param[in] itemSize: item size
 * @param[out] queueHandle: queue handle
 * @return  status codes:
 * - ERR_SUCCESS: create task successfully
 * - ERR_ERROR: create task unsuccessful
 */
errcode_t OS_Adapter_CreateQueue(uint32_t queueLength,
                                 uint32_t itemSize,
                                 queue_t queueHandle);
#endif /* configSUPPORT_STATIC_ALLOCATION == 1 */

#endif /* USING_OS_FREERTOS */

#if defined(__cplusplus)
}
#endif

#endif /*OS_ADAPTER_H*/

/*******EOF********************************************************************/
